package com.miaoa.jbox2d.demo;

import android.content.Context;
import android.graphics.RectF;
import android.util.TypedValue;

import org.jbox2d.callbacks.ContactFilter;
import org.jbox2d.callbacks.ContactImpulse;
import org.jbox2d.callbacks.ContactListener;
import org.jbox2d.callbacks.DestructionListener;
import org.jbox2d.collision.Manifold;
import org.jbox2d.collision.shapes.CircleShape;
import org.jbox2d.collision.shapes.EdgeShape;
import org.jbox2d.collision.shapes.PolygonShape;
import org.jbox2d.common.Vec2;
import org.jbox2d.dynamics.Body;
import org.jbox2d.dynamics.BodyDef;
import org.jbox2d.dynamics.BodyType;
import org.jbox2d.dynamics.FixtureDef;
import org.jbox2d.dynamics.World;
import org.jbox2d.dynamics.contacts.Contact;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
 * @Author Mlethe
 * @Date 2023/7/13
 */
public class JBox2DImpl {

    /**
     * 刚体最大数量
     */
    private final int MAX_BODY = 500;

    /**
     * 世界
     */
    private World mWorld;

    /**
     * 模拟世界的频率
     */
    private final float dt = 1f / 60f;

    /**
     * 速率迭代器
     */
    private final int mVelocityIterations = 5;

    /**
     * 迭代次数
     */
    private final int mPositionIterations = 20;

    /**
     * 刚体的密度
     */
    private float mDensity = 1f;

    /**
     * 刚体的摩擦系数
     */
    private final float mFriction = 5f;

    /**
     * 刚体的弹性系数
     */
    private final float mRestitution = 0.5f;

    /**
     * 坐标映射比例
     */
    private final float mRatio = 50f;

    /**
     * 世界与view的偏差值
     */
    private float mWorldOffset = 0f;

    /**
     * 图片与view的比例
     */
    private float mViewRatio = 0.9f;

    /**
     * 图片的宽度、高度
     */
    private float mImageWidth, mImageHeight;

    /**
     * 图片距离view左边、右边距离
     */
    private float mViewLeft, mViewTop;

    /**
     * 世界的宽度、高度
     */
    private float mWidth = 700f, mHeight = 700f;

    /**
     * 随机数
     */
    private final Random mRandom = new Random();

    /**
     * 自定义刚体数组
     */
    private final List<RigidBody> mBodyList = new ArrayList<>();

    /**
     * 边界的路线
     */
    private final ArrayList<RectF> mRectList = new ArrayList<>();

    /**
     * 创建世界
     *
     * @param width    图片宽度
     * @param height   图片宽度
     * @param viewLeft 图片距离view左边距离
     * @param viewTop  图片距离view顶部距离
     * @param ratio    比例
     */
    public synchronized void createWorld(Context context, float width, float height, float viewLeft, float viewTop, float ratio) {
        if (mWorld == null) {
            mImageWidth = width;
            mImageHeight = height;
            mViewLeft = viewLeft;
            mViewTop = viewTop;
            mViewRatio = ratio;
            mWidth = dp2px(context, 233.5f) * mViewRatio;
            mHeight = mWidth;
            mWorld = new World(new Vec2(0f, 10f));
            BodyDef bodyDef = new BodyDef();
            bodyDef.type = BodyType.STATIC;// 设置零重力,零速度
            bodyDef.position.set(0, 0);
            Body bodyTop = mWorld.createBody(bodyDef);// 世界中创建刚体
            FixtureDef fixtureDef = new FixtureDef();
            fixtureDef.density = mDensity;//物质密度
            fixtureDef.friction = mFriction;//摩擦系数
            fixtureDef.restitution = mRestitution;//恢复系数
            //设置世界边界（设置刚体边界）
            // 顶部边界
            mWorldOffset = dp2px(context, 12f) * (1 - mViewRatio);
            float top = mViewTop + dp2px(context, 58f) * mViewRatio + mWorldOffset;
            float offsetWidth = dp2px(context, 20f) * mViewRatio;
            float left = mViewLeft + (mImageWidth - mWidth) / 2f + mWorldOffset;
            float lineLeft = left + offsetWidth;
            EdgeShape shape = new EdgeShape();
            shape.set(new Vec2(pixelsToMeters(lineLeft), pixelsToMeters(top)), new Vec2(pixelsToMeters(lineLeft + mWidth - offsetWidth * 2), pixelsToMeters(top)));
            addRect(lineLeft, top, lineLeft + mWidth - offsetWidth * 2, top);
            fixtureDef.shape = shape;
            bodyTop.createFixture(fixtureDef);//刚体添加夹具
            // 顶部左上角竖线
            float dp8 = dp2px(context, 8f) * mViewRatio;
            float lineBottom = top + dp8;
            shape.set(new Vec2(pixelsToMeters(lineLeft), pixelsToMeters(top)), new Vec2(pixelsToMeters(lineLeft), pixelsToMeters(lineBottom)));
            addRect(lineLeft, top, lineLeft, lineBottom);
            fixtureDef.shape = shape;
            bodyTop.createFixture(fixtureDef);//刚体添加夹具
            // 顶部左上角圆弧
            float dp117 = dp2px(context, 117f) * mViewRatio;
            float dp43 = dp2px(context, 43.5f) * mViewRatio;
            float dp1 = dp2px(context, 1f) * mViewRatio;
            float radius = dp117;
            float radianTop = top - dp43;
            float radianLeft = left + dp1;
            ArrayList<float[]> floats = radianCalculation(8, radius, 180f, 33f);
            for (float[] xy : floats) {
                float x = xy[0];
                float y = xy[1];
                float segmentLength = xy[2];
                float angle = xy[3];
                PolygonShape polygonShape = new PolygonShape();
                // 设置具有方向的shape
                polygonShape.setAsBox(0, pixelsToMeters(segmentLength), new Vec2(pixelsToMeters(radianLeft + x), pixelsToMeters(radianTop + y)), angle);
                fixtureDef.shape = polygonShape;
                addRect(polygonShape);
                bodyTop.createFixture(fixtureDef);//刚体添加夹具
            }
            // 左边竖线
            float dp73 = dp2px(context, 73.5f) * mViewRatio;
            float dp35 = dp2px(context, 35f) * mViewRatio;
            float lineTop = top + dp73;
            lineBottom = top + mHeight - dp35;
            shape.set(new Vec2(pixelsToMeters(radianLeft), pixelsToMeters(lineTop)), new Vec2(pixelsToMeters(radianLeft), pixelsToMeters(lineBottom)));
            addRect(radianLeft, lineTop, radianLeft, lineBottom);
            fixtureDef.shape = shape;
            bodyTop.createFixture(fixtureDef);//刚体添加夹具
            // 左下角圆弧
            radius = dp35;
            float dp7 = dp2px(context, 7.5f) * mViewRatio;
            radianTop = lineBottom - radius - dp7;
            floats = radianCalculation(10, radius, 100f, 80f);
            for (float[] xy : floats) {
                float x = xy[0];
                float y = xy[1];
                float segmentLength = xy[2];
                float angle = xy[3];
                PolygonShape polygonShape = new PolygonShape();
                // 设置具有方向的shape
                polygonShape.setAsBox(0, pixelsToMeters(segmentLength), new Vec2(pixelsToMeters(radianLeft + x), pixelsToMeters(radianTop + y)), angle);
                fixtureDef.shape = polygonShape;
                addRect(polygonShape);
                bodyTop.createFixture(fixtureDef);//刚体添加夹具
            }
            // 顶部右边界
            lineBottom = top + dp8;
            shape.set(new Vec2(pixelsToMeters(lineLeft + mWidth - offsetWidth * 2), pixelsToMeters(top)), new Vec2(pixelsToMeters(lineLeft + mWidth - offsetWidth * 2), pixelsToMeters(lineBottom)));
            fixtureDef.shape = shape;
            addRect(lineLeft + mWidth - offsetWidth * 2, top, lineLeft + mWidth - offsetWidth * 2, lineBottom);
            bodyTop.createFixture(fixtureDef);//刚体添加夹具
            // 顶部右上角圆弧
            radius = dp117;
            radianTop = top - dp43;
            radianLeft = left - dp1;
            floats = radianCalculation(15, radius, 326f, 34f);
            for (float[] xy : floats) {
                float x = xy[0];
                float y = xy[1];
                float segmentLength = xy[2];
                float angle = xy[3];
                PolygonShape polygonShape = new PolygonShape();
                // 设置具有方向的shape
                polygonShape.setAsBox(0, pixelsToMeters(segmentLength), new Vec2(pixelsToMeters(radianLeft + x), pixelsToMeters(radianTop + y)), angle);
                fixtureDef.shape = polygonShape;
                addRect(polygonShape);
                bodyTop.createFixture(fixtureDef);//刚体添加夹具
            }
            // 右边竖线
            radianLeft = left + mWidth;
            lineTop = top + dp73;
            lineBottom = top + mHeight - dp35;
            shape.set(new Vec2(pixelsToMeters(radianLeft), pixelsToMeters(lineTop)), new Vec2(pixelsToMeters(radianLeft), pixelsToMeters(lineBottom)));
            addRect(radianLeft, lineTop, radianLeft, lineBottom);
            fixtureDef.shape = shape;
            bodyTop.createFixture(fixtureDef);//刚体添加夹具
            // 右下角圆弧
            radius = dp35;
            radianTop = lineBottom - radius - dp7;
            radianLeft = left + mWidth - radius * 2;
            floats = radianCalculation(10, radius, 0f, 80f);
            for (float[] xy : floats) {
                float x = xy[0];
                float y = xy[1];
                float segmentLength = xy[2];
                float angle = xy[3];
                PolygonShape polygonShape = new PolygonShape();
                // 设置具有方向的shape
                polygonShape.setAsBox(0, pixelsToMeters(segmentLength), new Vec2(pixelsToMeters(radianLeft + x), pixelsToMeters(radianTop + y)), angle);
                fixtureDef.shape = polygonShape;
                addRect(polygonShape);
                bodyTop.createFixture(fixtureDef);//刚体添加夹具
            }
            // 底部圆弧
            radius = dp2px(context, 467f) * mViewRatio;
            radianTop = top - radius - mHeight;
            radianLeft = mViewLeft + (mImageWidth - radius * 2) / 2 + mWorldOffset;
            floats = radianCalculation(10, radius, 80f, 20f);
            for (float[] xy : floats) {
                float x = xy[0];
                float y = xy[1];
                float segmentLength = xy[2];
                float angle = xy[3];
                PolygonShape polygonShape = new PolygonShape();
                // 设置具有方向的shape
                polygonShape.setAsBox(0, pixelsToMeters(segmentLength), new Vec2(pixelsToMeters(radianLeft + x), pixelsToMeters(radianTop + y)), angle);
                fixtureDef.shape = polygonShape;
                addRect(polygonShape);
                bodyTop.createFixture(fixtureDef);//刚体添加夹具
            }
        }
    }

    /**
     * 根据半径获取多边形每个点的坐标位置
     *
     * @param size       多边形边数
     * @param radius     半径
     * @param startAngle 开始角度
     * @param sweepAngle 绘制的角度
     * @return
     */
    public ArrayList<float[]> radianCalculation(int size, float radius, float startAngle, float sweepAngle) {
        float ratio = sweepAngle / 360f;
        // 弧长 = 半径 * 弧度，弧度 = sin(2 * Math.PI / size)
        float segmentLength = (float) (Math.PI * radius * ratio / size);
        ArrayList<float[]> doubles = new ArrayList<>();
        float theta = sweepAngle / size;
        for (int i = 0; i < size + 1; i++) {
            float x, y = 0f;
            double angle = Math.PI / 180 * (startAngle + theta * i);
            x = (float) (radius * Math.cos(angle));
            y = (float) (radius * Math.sin(angle));
            float[] xy = new float[4];
            xy[0] = x + radius;
            xy[1] = y + radius;
            xy[2] = segmentLength;
            xy[3] = (float) angle;
            doubles.add(xy);
        }
        return doubles;
    }

    /**
     * 获取绘制的 Rect
     *
     * @return
     */
    public ArrayList<RectF> getRectList() {
        return mRectList;
    }

    /**
     * 添加绘制的 Rect
     *
     * @param left
     * @param top
     * @param right
     * @param bottom
     */
    private void addRect(float left, float top, float right, float bottom) {
//        mRectList.add(new RectF(left, top, right, bottom));
    }

    /**
     * 添加绘制的 Rect
     *
     * @param shape
     */
    private void addRect(PolygonShape shape) {
//        float left = metersToPixels(shape.m_vertices[0].x);
//        float right = metersToPixels(shape.m_vertices[2].x);
//        float top = metersToPixels(shape.m_vertices[0].y);
//        float bottom = metersToPixels(shape.m_vertices[2].y);
//        addRect(left, top, right, bottom);
    }

    /**
     * 创建刚体
     *
     * @param rigidBody 刚体对象
     * @param top       顶部位置
     */
    public void createBody(RigidBody rigidBody, float top) {
        if (mWorld == null) return;
        if (mBodyList.size() >= MAX_BODY) return;
        BodyDef bodyDef = new BodyDef();
        bodyDef.type = BodyType.DYNAMIC;
        bodyDef.gravityScale = 0f;
        // 设置初始参数，为view的中心点
        bodyDef.position.set(pixelsToMeters(mViewLeft + mImageWidth / 2f), pixelsToMeters(top + mWorldOffset));
        CircleShape shape = new CircleShape();
        shape.setRadius(pixelsToMeters(rigidBody.getWidth() * mViewRatio / 2f));
        FixtureDef fixture = new FixtureDef();
        fixture.setShape(shape);
        fixture.density = mDensity;// 物质密度
        fixture.friction = mFriction;// 摩擦系数
        fixture.restitution = mRestitution;// 恢复系数

        // 用世界创建出刚体
        Body body = mWorld.createBody(bodyDef);
        body.createFixture(fixture);
        rigidBody.setBody(body);
        // 初始化物体的运动行为
        body.setLinearVelocity(new Vec2(mRandom.nextInt(10) - mRandom.nextInt(10), 10f));
        mBodyList.add(rigidBody);
    }

    /**
     * 刚体的密度
     *
     * @param density
     */
    public void setDensity(float density) {
        this.mDensity = density;
    }


    /**
     * 销毁刚体
     *
     * @param rigidBody
     */
    public void destroyBody(RigidBody rigidBody) {
        try {
            if (rigidBody == null || mWorld == null) return;
            mWorld.destroyBody(rigidBody.getBody());
        } catch (Throwable t) {
            t.printStackTrace();
        }
    }


    /**
     * 销毁刚体
     *
     * @param position
     */
    public void removeBody(int position) {
        try {
            RigidBody rigidBody = mBodyList.remove(position);
            if (rigidBody == null || mWorld == null) return;
            mWorld.destroyBody(rigidBody.getBody());
        } catch (Throwable t) {
            t.printStackTrace();
        }
    }

    /**
     * 销毁指定位置之后的刚体
     *
     * @param startPosition 开始位置
     * @param count         删除数量
     */
    public void removeBody(int startPosition, int count) {
        if (startPosition < 0) return;
        int size = mBodyList.size() - 1;
        int start = startPosition + count - 1;
        if (start > size) {
            start = size;
        }
        for (int i = start; i >= startPosition; i--) {
            removeBody(i);
        }
    }

    /**
     * 销毁指定位置之后的刚体
     *
     * @param startPosition 开始位置
     */
    public void removeBodyStart(int startPosition) {
        removeBody(startPosition, mBodyList.size() - startPosition);
    }

    /**
     * 销毁所有刚体
     */
    public void removeAllBody() {
        removeBodyStart(0);
    }

    /**
     * 销毁最后指定数量的刚体
     *
     * @param count 删除数量
     */
    public void removeLastBody(int count) {
        if (count <= 0) return;
        int size = mBodyList.size();
        int endPosition = size - count;
        if (endPosition < 0) {
            endPosition = 0;
        }
        for (int i = size - 1; i >= endPosition; i--) {
            removeBody(i);
        }
    }

    /**
     * 设置碰撞监听
     *
     * @param listener 监听
     */
    public void setContactListener(ContactListener listener) {
        if (mWorld != null) {
            mWorld.setContactListener(listener);
        }
    }

    /**
     * 设置碰撞过滤器
     *
     * @param filter 过滤器
     */
    public void setContactFilter(ContactFilter filter) {
        if (mWorld != null) {
            mWorld.setContactFilter(filter);
        }
    }

    /**
     * 设置摧毁监听器
     *
     * @param listener 监听
     */
    public void setDestructionListener(DestructionListener listener) {
        if (mWorld != null) {
            mWorld.setDestructionListener(listener);
        }
    }

    /**
     * 销毁数据
     */
    public void destroy() {
        if (mWorld == null) return;
        removeAllBody();
        mWorld.setDestructionListener(null);
        mWorld.setContactFilter(null);
        mWorld.setContactListener(null);
    }

    /**
     * 设置重力
     *
     * @param x
     * @param y
     */
    public void applyLinearImpulse(float x, float y) {
        for (RigidBody rigidBody : mBodyList) {
            applyLinearImpulse(x, y, rigidBody);
        }
    }

    /**
     * 设置重力
     *
     * @param x
     * @param y
     * @param rigidBody
     */
    private void applyLinearImpulse(float x, float y, RigidBody rigidBody) {
        if (rigidBody == null) return;
        Body body = rigidBody.getBody();
        Vec2 impulse = new Vec2(x, y);
        // 给body做线性运动 true 运动完之后停止
        body.applyLinearImpulse(impulse, body.getPosition(), true);
    }

    /**
     * 开启世界
     */
    public void startWorld() {
        if (mWorld == null) return;
        mWorld.step(dt, mVelocityIterations, mPositionIterations);
    }

    /**
     * 获取刚体列表
     *
     * @return
     */
    public List<RigidBody> getBodyList() {
        return mBodyList;
    }

    /**
     * 获取刚体转换后的x轴位置
     *
     * @param body
     * @return
     */
    public float getBodyX(RigidBody body) {
        return metersToPixels(body.getBody().getPosition().x) - body.getWidth() / 2f;
    }

    /**
     * 获取刚体转换后的y轴位置
     *
     * @param body
     * @return
     */
    public float getBodyY(RigidBody body) {
        return metersToPixels(body.getBody().getPosition().y) - body.getHeight() / 2f;
    }

    /**
     * 获取刚体旋转角度
     *
     * @param body
     * @return
     */
    public double getBodyAngle(RigidBody body) {
        return radiansToDegrees(body.getBody().getAngle()) % 360;
    }

    public float metersToPixels(float meters) {
        return meters * mRatio;
    }

    public float pixelsToMeters(float pixels) {
        return pixels / mRatio;
    }

    /**
     * 弧度转角度
     *
     * @param radians
     * @return
     */
    private double radiansToDegrees(float radians) {
        return radians / Math.PI * 180;
    }

    /**
     * dp转px
     *
     * @param dp
     * @return
     */
    public static int dp2px(Context context, float dp) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, context.getResources().getDisplayMetrics());
    }

    /**
     * sp转px
     *
     * @param sp
     * @return
     */
    public static int sp2px(Context context, float sp) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, context.getResources().getDisplayMetrics());
    }
}
